from django.core.cache import cache

from utils.ac_automaton import ACAutomaton
from violation.models import Violation, JobAuditRecord


class JObAuditService:
    # 职位审核的核心服务
    cache_key = "job_audit_service"  # 缓存到redis的key(缓存名)

    @staticmethod
    def _normalize_text(content: str) -> str:
        """去除空白字符，避免通过插入空格规避违规词检测。"""
        if not content:
            return ""
        # 删除空格、换行、制表等空白符
        return "".join(str(content).split())

    @classmethod
    def _load_ac_words(cls):
        """私有方法：从数据库加载违规词，构建AC自动机并缓存到Redis"""
        # 1. 从数据库查询所有"启用"的违规词（status=1）
        ac_words = Violation.objects.filter(status=1).values_list("word", flat=True)
        # 构建AC自动机
        ac = ACAutomaton()  # 修复：需要实例化
        for word in ac_words:
            ac.add_word(word.strip())  # 添加违规词到违规词列表中(去掉空格)
        ac.build_fail_links()  # 构建失败指针
        # 把AC自动机缓存到redis，有效期一小时（如果Redis可用）
        try:
            cache.set(cls.cache_key, ac, 3600)
        except Exception as e:
            # Redis连接失败时，记录日志但不影响功能
            import logging
            logging.warning(f"Redis缓存失败，将继续使用内存中的AC自动机: {str(e)}")
        return ac

    @classmethod
    def get_ac_automaton(cls):
        """获取AC自动机（优先从Redis缓存取，缓存过期了再重新加载）"""
        ac = None
        try:
            ac = cache.get(cls.cache_key)  # 从redis获取缓存
        except Exception as e:
            # Redis连接失败时，记录日志并继续从数据库加载
            import logging
            logging.warning(f"Redis缓存获取失败，将从数据库重新加载: {str(e)}")
        
        if not ac:  # 若缓存不存在（比如第一次审核、缓存过期、Redis不可用）
            ac = cls._load_ac_words()  # 重新加载违规词，构建AC自动机
        return ac

    @classmethod
    def refresh_ac_word_cache(cls):
        """主动刷新缓存（比如运营人员在后台添加了新违规词，需要立即生效）"""
        cls._load_ac_words()
        return True

    @classmethod
    def audit_plain_text(cls, content: str):
        """
        审核任意文本内容（不落库），供简历等场景复用。

        :param content: 待审核的文本（将自动去除空白字符再检测）
        :return: {
            "passed": bool,
            "violation": [违规词列表],
            "word_types": [违规类型中文],
            "msg": str
        }
        """
        normalized_content = cls._normalize_text(content)
        if not normalized_content:
            return {
                "passed": True,
                "violation": [],
                "word_types": [],
                "msg": "内容为空，无需审核"
            }

        ac = cls.get_ac_automaton()
        violation = ac.search(normalized_content)

        if not violation:
            return {
                "passed": True,
                "violation": [],
                "word_types": [],
                "msg": "审核通过"
            }

        word_type_ids = Violation.objects.filter(
            word__in=violation
        ).values_list("word_type", flat=True).distinct()
        word_types = [dict(Violation.WORD_TYPE_CHOICES)[tid] for tid in word_type_ids]

        return {
            "passed": False,
            "violation": violation,
            "word_types": word_types,
            "msg": f"发现违规词: {', '.join(violation)}"
        }

    @classmethod
    def audit_job_content(cls, job_id, company_id, job_title, job_description, audit_ip=None):
        """
        核心方法：审核职位内容
        :param job_id: 职位ID（哪个职位）
        :param company_id: 企业ID（哪个企业）
        :param job_title: 职位标题
        :param job_description: 职位描述
        :param audit_ip: 审核请求IP（谁发起的）
        :return: 审核结果字典（给前端看）
        """
        # 1. 拼接审核内容： 标题+描述， 去除空格和换行（避免“高 薪 日 结”这样的规避方式）
        audit_content = cls._normalize_text(f"{job_title.strip()} {job_description.strip()}")
        if not audit_content:  # 如果拼接后文本为空（比如职位标题和描述都没填）
            return {
                "audit_result": 3,  # 数据库中3为可疑，需复审
                "illegal_words": [],  # 没有违规词
                "word_types": [],  # 没有违规词类型
                "msg": "审核内容为空，需复审"
            }
        # 调用AC自动机，找违规词
        ac = cls.get_ac_automaton()  # 获取AC自动机
        violation = ac.search(audit_content)  # 匹配违规词

        # 处理审核结果
        if not violation:  # 为空既是没有违规词
            audit_result = 1  # 合规
            word_types = []
            msg = "审核通过"
        else:  # 找到违规词
            audit_result = 2  # 违规
            # 查找违规词对应的类型（比如“高薪日结”属于“虚假招聘”）
            word_type_ids = Violation.objects.filter(word__in=violation).values_list("word_type", flat=True).distinct()
            # 把类型ID转成中文(如类型ID为1， 转成虚假招聘)
            word_types = [dict(Violation.WORD_TYPE_CHOICES)[tid] for tid in word_type_ids]
            msg = f"发现违规词:{','.join(violation)}"

        # 保存审核记录到数据库(方便后续查找)
        JobAuditRecord.objects.create(
            job_id=job_id,  # 职位ID
            company_id=company_id,  # 企业ID
            job_title=job_title,  # 职位标题
            job_description=job_description,  # 职位描述
            audit_content=audit_content,  # 实际审核内容（标题+描述）
            illegal_words=",".join(violation) if violation else None,  # 违规词用逗号拼接
            word_types=",".join(word_types) if word_types else None,  # 违规词类型用逗号拼接
            audit_result=audit_result,  # 审核结果
            audit_ip=audit_ip  # 审核IP
        )

        # 返回审核结果
        return {
            "job_id": job_id,
            "audit_result": audit_result,
            "audit_result_desc": dict(JobAuditRecord.AUDIT_RESULT_CHOICES)[audit_result],  # 结果中文描述
            "violation": violation,
            "word_types": word_types,
            "msg": msg
        }
